Skip to content

Add Custom OpenCV and ONNX Runtime Builds and OCR performance optimization - #57

Merged
283375 merged 32 commits into
masterfrom
refactor/custom-libs
Aug 29, 2026
Merged

Add Custom OpenCV and ONNX Runtime Builds and OCR performance optimization#57
283375 merged 32 commits into
masterfrom
refactor/custom-libs

Conversation

@283375

@283375 283375 commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator
  • Migrate to OpenCV 5, remove KNearest digit OCR logics
  • Use custom OpenCV and ONNX Runtime builds from https://github.com/ArcaeaOffline/custom-lib-builds
  • Add "OCR Performance" quick evaluation page
  • Fix OCR performance: hammingDistance is now allocation-free in image hash lookups, previous zip-based version caused severe GC pressure. Now queue throughput is roughly doubled

283375 added 26 commits August 2, 2026 22:05
- delete OcrDigits.kt (HOG feature extraction + findNearest helpers)
- remove kNearestModel parameter from DeviceOcr constructor
- remove pfl/pure/far/lost/score/maxRecall methods (dead code since ONNX migration)
- remove FixRects (only used by removed KNN digit pipeline)
- remove kNearestModel parameter from DeviceOcrHelper.ocrImage
- stop loading KNN model in OcrQueueOcrImageTaskExecutor and OcrFromShareViewModel.startOcr
- drop kNearestModel() loaders, KNearestModelStatusDetail and status builder
- remove knnModelFile path constant and its deletion in emergency mode
- add LegacyKnnModelCleanUpTask to clean up leftover digits.knn.dat
  on devices that installed before the removal
- delete OcrDependencyKNearestModelStatusViewer (status card + ui state)
- remove KNearest status cards from OcrNavEntry, OcrFromShare card and OcrDependenciesScreen
- remove importKNearestModel and related state from OcrDependenciesScreenViewModel
- delete ocr_dependency_knn_model strings (app + shared composeResources, zh-rCN)
- delete ic_knearest_model drawable
- OpenCV 5.0 removes ml (KNearest) and HOG from the main repo;
  both were already removed from this codebase, so no migration needed
- usage limited to stable core/imgproc/imgcodecs/android APIs,
  verified by Android Studio build (no problems)
Database version jumped from 2 to 4 in 80139db; version 3 was never
published, but the AutoMigration was generated against a fictional
3.json (enqueue_buffer with uri_type column) which crashes on real
v2 databases (no such column: uri_type).

Remove the AutoMigration and the spec; old databases now hit the
existing fallbackToDestructiveMigration(dropAllTables = true) path
and are rebuilt cleanly as v4.
Switch to the local maven repo (maven-local/) that overrides official
onnxruntime 1.26.0 and opencv 5.0.0 with custom-built AARs.
Parse dual-block model_info.json for the dependency status card instead of
OnnxModelMetadata, disable graph optimization (NO_OPT) for the reduced-op
runtime, and read INT32 model output directly.
Add setup-custom-maven action that downloads maven_repo.zip from the
custom-lib-builds stable release tags (onnxruntime-1.26.0, opencv-5.0.0)
into maven-local/, overriding official artifacts with identical GAV.
Wire it into build_unstable, check, and connected-android-test workflows.

No cache: stable tags are overwritten on publish (URL stable, content
changes), so version-based cache keys would serve stale artifacts.
Compare .so sha256 between maven-local/ AARs and built APKs, guarding
against silent fallback to official onnxruntime/opencv artifacts.
Warnings go to annotations and the job summary; continue-on-error
keeps artifact upload and release drafting unblocked.
Move the download/extract logic out of the composite action and the
apksigner checks out of the workflow, so both are runnable and testable
locally. setup-custom-maven and verify-apk-signing keep hard-fail
semantics; verify-custom-libs remains soft-fail. Cleaned up shellcheck
findings along the way.
- Previous commit was mislead, this commit actually just fixes it.
@283375 283375 changed the title Add Custom OpenCV and ONNX Runtime Builds Add Custom OpenCV and ONNX Runtime Builds and OCR performance optimization Aug 18, 2026
Comment thread app/src/main/java/xyz/sevive/arcaeaoffline/datastore/OcrQueuePreferences.kt Outdated

@283375 283375 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs further investigations

283375 added 6 commits August 28, 2026 17:45
Addresses PR #57 review: the device-derived parallelCount default and
range now live in a dedicated OcrQueueParallelism object instead of the
companion of the @serializable preferences class, keeping runtime
strategy separate from the preferences schema. No behavior change.
Addresses PR #57 review: replace the byte-level checkModelAsset with
verifyModelAsset, which creates a throwaway ORT session (validating the
file itself and the custom build's op coverage) and cross-checks its
metadata against model_info.json — version (semver-decoded), producer,
domain, graph name, input/output names, and the custom metadata entries
written by the exporter. All mismatches are collected into a single
IllegalStateException surfaced on the dependency status card.

The comparison logic lives in a pure collectMetadataMismatches function
covered by JVM unit tests (8 cases); the removed modelVersion dead code
is revived as onnxModelVersion and put to use.
- format report/history/log numbers with Locale.ROOT so decimal
  separators stay consistent across device locales
- use a strict median over timed batches instead of the upper-middle
  element for even batch counts
- read the parallel count once and reuse it for runningParallel,
  runBenchmark and history, instead of two independent reads
- collapse preferences init into a single atomic state update
- unify the running-state reset in a finally block across exit paths
- poll cancellation between image decodes, which contain no suspension
  points
- remove What-style comments; document why the report is not localized
@283375

283375 commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator Author

/opencode review this pr

@opencode-agent

Copy link
Copy Markdown

I've reviewed the full diff (56 files) against master. Here's my review.

Summary

Solid, well-organized PR. The KNearest removal is clean (all call sites updated, no dangling references), the hammingDistance allocation fix is the clear performance win, and the .use-based Mat lifecycle cleanup is thorough and correct — ownership transfers I checked all line up (extractor getters return fresh submat headers, maskers return owned Mats, ocrBgrMat/tensors/results are closed on all paths). The verifyModelAsset metadata cross-check and the benchmark screen are nice additions with good tests.

Issues

1. Room 3→4 migration removal can destroy user data (medium, verify)OcrQueueDatabase.kt:26
DB stays at version = 4 but the only 3→4 path is removed and 3.json + the AutoMigrationSpec are deleted. Combined with fallbackToDestructiveMigration(dropAllTables = true), any device still on schema 3 gets its whole OCR queue DB dropped. Commit b7e9f7c asserts v3 "doesn't exist", but 3.json is checked in on master — please confirm v3 was never in a shipped release; if it was, this is a data-loss regression.

2. CI never validates the real model asset against model_info.json (medium)ModelMetadataVerificationTest.kt
The test hardcodes expected values, so it can't catch drift between the HuggingFace model_info.json and the patched ONNX. A stale/updated asset would only surface at runtime as an ERROR status on the dependencies screen. Consider a CI step that runs verifyModelAsset against the actual downloaded assets.

3. Model verification doesn't run inference (low)DeviceOcrOnnxHelper.kt:97-122
verifyModelAsset only creates the session and reads metadata. ORT typically reports missing kernels at Run() time, not session creation, so the "op coverage" claim is only partially validated — issues still appear on first OCR. Also, modelDecodedOutputToString (DeviceOcrOnnxHelper.kt:158) now assumes int32 output via intBuffer; the verification checks names but not the output dtype, so a dtype change would crash at runtime silently.

4. NO_OPT disables all graph optimization (low)DeviceOcrOnnxHelper.kt:132
Necessary for the custom build, but it's an all-or-nothing knob; a comment noting the option to enable it if the custom build adds the missing optimized nodes would help future maintainers. Worth quantifying whether the ~2x queue throughput claim nets out against this on the benchmark page.

5. Double-tap race in runBenchmark() (low)OcrPerformanceScreenViewModel.kt:98-101
if (state.running) return reads a snapshot before the coroutine sets running=true. Two rapid taps in the same frame can start concurrent benchmarks (each spawning its own session + ROI sets). Move the check inside the coroutine or gate on benchmarkJob?.isActive.

6. Soft-fail undermines the "hard-fail" intent (low).github/workflows/build_unstable.yml:47-49
verify-custom-libs.sh exits 1 with continue-on-error: true, so a regression back to official (larger) libs still ships. The comment in setup-custom-maven.sh calls download failure a hard-fail, but the release-side check is deliberately soft — worth confirming that's intentional and that the job-summary warning is sufficiently visible.

7. CropBlackEdges off-by-one (pre-existing, touched here)CropBlackEdges.kt:47-68
Loops 0..width/0..height are inclusive; Rect(width, 0, 1, height) is out of bounds if the black edge runs to the last pixel. Pre-existing behavior, but these lines were rewritten — easy to fix while you're here.

Nits

  • LegacyKnnModelCleanUpTask.version = 2 is odd for a brand-new task; the manager runs tasks on every launch regardless, so it's harmless but confusing.
  • ImageHashesDatabase.lookupImage (ImageHashesDatabase.kt:185-187): if difference/dct throws before the try, an earlier hash Mat leaks. Trivial; could move all three into the try.
  • CrnnModelStatusDetail.status() dropped the version == 0 → WARNING case; "0.0.0" now reports OK.
  • OcrDependencyStatusBuilder.crnnModel: empty-string domain renders as an empty "domain: " line.

Positives

  • The hammingDistance loop (ImageHashesDatabase.kt:9-21) is exactly right for a per-entry hot path — the zip/count version was the right thing to kill.
  • Thorough and consistent Mat cleanup across DeviceOcr, hashers, CropBlackEdges, and the ONNX helper; I found no double-releases or use-after-release.
  • collectMetadataMismatches being a pure function with JVM tests is good design.
  • The benchmark correctly mirrors the production Channel-based concurrency model, uses median over mean, and validates output consistency — useful for tuning parallelCount.
  • Custom-maven setup/verification scripts are well-documented and version-aware.

New%20session%20-%202026-08-29T08%3A21%3A46.652Z
opencode session  |  github run

@283375
283375 merged commit f1f314d into master Aug 29, 2026
4 checks passed
@283375
283375 deleted the refactor/custom-libs branch August 29, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant